package com.example.pacs.dicom.server.cstoresup;

import com.example.pacs.constants.PacsConstants;
import com.example.pacs.dicom.event.NewFileEvent;
import com.google.common.eventbus.EventBus;
import lombok.extern.slf4j.Slf4j;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.VR;
import org.dcm4che3.io.DicomOutputStream;
import org.dcm4che3.net.Association;
import org.dcm4che3.net.PDVInputStream;
import org.dcm4che3.net.pdu.PresentationContext;
import org.dcm4che3.net.service.BasicCStoreSCP;
import org.dcm4che3.util.SafeClose;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;

/**
 * SUP对于C-STORE的实现
 */
@Slf4j
@Component
public class SUPCStoreServiceImpl extends BasicCStoreSCP {

    public SUPCStoreServiceImpl () {
        super("*");
        
    }

    /**
     * dcm文件存放的位置
     */
    @Value("${pacs.storage.dir}")
    private String pacsStorageDir;

    /**
     * 这里的eventBus是我们在配置类里面注入IOC容器的,因为bean的scope默认是单例的,所以注入的都是同一个bean
     */
    @Resource
    private EventBus eventBus;

    /**
     * 接受dcm文件实现
     *
     * @param as association,DICOM建立的连接,类似于传统的connection
     * @param pc PresentationContext,dicom建立连接后传输的消息,称为描述上下文,其中包括Abstract Syntax(SOP CLASS UID,具体服务),transfer syntax:编码模式
     * @param rq 连接请求
     * @param data 具体传输的数据,DICOM传输过程中DIMSE消息会被分割成多个PDV进行传递
     * @param rsp 连接响应
     */
    @Override
    protected void store(Association as, PresentationContext pc, Attributes rq, PDVInputStream data, Attributes rsp) {
        log.info("进入dcm文件保存,as:{}", as.toString());
        // 设置响应编码,0代表成功
        rsp.setInt(Tag.Status, VR.US, 0);
        // 判断是否设置了存放dcm文件的目录
        if (StringUtils.isEmpty(pacsStorageDir)) {
            return;
        }
        // 从连接中获取IP地址
        String hostAddress = as.getSocket().getInetAddress().getHostAddress();
        // 从请求连接中获取Affected SOP CLASS UID
        String cuid = rq.getString(Tag.AffectedSOPClassUID);
        // 从连接中获取Affected SOP Instance UID
        String iuid = rq.getString(Tag.AffectedSOPInstanceUID);
        // 从描述上下文中获取编码方式
        String tsuid = pc.getTransferSyntax();
        // 使用instance UID加后缀生成文件名,放在配置存储dcm文件的路径下
        File file = new File(pacsStorageDir, iuid + PacsConstants.DCM_SUFFIX);
        // 该方法通过出入参数和填充默认参数构建一个Attributes
        Attributes attributes = as.createFileMetaInformation(iuid, cuid, tsuid);
        try {
            storeTo(attributes, data, file);
            // 判断文件是否存在
            if (!file.exists()) {
                log.error("File:{} not exists! hostAddress:{}, cuid:{}, iuid:{}, tsuid:{}", file.getAbsolutePath(), hostAddress, cuid, iuid, tsuid);
                return;
            }
            // 给所有关注新建文件的监听者发布新建文件事件
            eventBus.post(new NewFileEvent(file));
        } catch (Exception e) {
            log.error("store error! message is :" + e.getMessage(), e);
        }
    }

    /**
     * 将文件存储到对应的配置的目录
     *
     * @param fmi 元数据
     * @param data dicom message被具体分割的片段
     * @param file 预创建的file
     */
    private void storeTo(Attributes fmi, PDVInputStream data, File file) throws IOException {
        // 创建文件夹
        file.getParentFile().mkdirs();
        // 构建DICOM输出流
        DicomOutputStream dicomOutputStream = new DicomOutputStream(file);
        try {
            dicomOutputStream.writeFileMetaInformation(fmi);
            data.copyTo(dicomOutputStream);
        } finally {
            SafeClose.close(dicomOutputStream);
        }
    }
}
